package com.seedwill.common.oxm;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.ConverterLookup;
import com.thoughtworks.xstream.converters.ConverterMatcher;
import com.thoughtworks.xstream.converters.ConverterRegistry;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;

/**
 * XStream初始化服务
 * 
 * 用于OBJECT与OML互转
 * 
 * @author kdyi
 * 
 */
public class XStreamInitialiser extends ApplicationObjectSupport implements
		FactoryBean {
	/**
	 * The default encoding used for stream access: UTF-8.
	 */
	public static final String DEFAULT_ENCODING = "UTF-8";

	private String encoding = DEFAULT_ENCODING;

	/**
	 * 是否启用HIBERNATE PO转换
	 */
	private boolean enableHibernatePO = true;

	public boolean isEnableHibernatePO() {
		return enableHibernatePO;
	}

	public void setEnableHibernatePO(boolean enableHibernatePO) {
		this.enableHibernatePO = enableHibernatePO;
	}

	@SuppressWarnings("unchecked")
	private Class[] supportedClasses;

	@SuppressWarnings("unchecked")
	public Class[] getSupportedClasses() {
		return supportedClasses;
	}

	@SuppressWarnings("unchecked")
	public void setSupportedClasses(Class[] supportedClasses) {
		this.supportedClasses = supportedClasses;
	}

	@SuppressWarnings("unchecked")
	public List<Class> getUseAttributeForTypes() {
		return useAttributeForTypes;
	}

	@SuppressWarnings("unchecked")
	public void setUseAttributeForTypes(List<Class> useAttributeForTypes) {
		this.useAttributeForTypes = useAttributeForTypes;
	}

	@SuppressWarnings("unchecked")
	private List<Class> useAttributeForTypes;

	/**
	 * XStream mode
	 */
	private int mode;

	public int getMode() {
		return mode;
	}

	/**
	 * Set the XStream mode.
	 * 
	 * @see XStream#XPATH_REFERENCES
	 * @see XStream#ID_REFERENCES
	 * @see XStream#NO_REFERENCES
	 */
	public void setMode(int mode) {
		this.mode = mode;
	}

	/**
	 * 转换器
	 */
	private List<ConverterMatcher> converters;

	public List<ConverterMatcher> getConverters() {
		return converters;
	}

	public void setConverters(List<ConverterMatcher> converters) {
		this.converters = converters;
	}

	/**
	 * package 别名
	 */
	private Map<String, String> aliasPkg;

	public Map<String, String> getAliasPkg() {
		return aliasPkg;
	}

	public void setAliasPkg(Map<String, String> aliasPkg) {
		this.aliasPkg = aliasPkg;
	}

	/**
	 * 别名
	 */
	@SuppressWarnings("unchecked")
	private Map<String, Class> aliases;

	@SuppressWarnings("unchecked")
	public Map<String, Class> getAliases() {
		return aliases;
	}

	@SuppressWarnings("unchecked")
	public void setAliases(Map<String, Class> alias) {
		this.aliases = alias;
	}

	/**
	 * 属性
	 */
	private Map<?, ?> attributes;

	/**
	 * 反射提供
	 */
	private ReflectionProvider reflectionProvider = null;

	public ReflectionProvider getReflectionProvider() {
		return reflectionProvider;
	}

	public void setReflectionProvider(ReflectionProvider reflectionProvider) {
		this.reflectionProvider = reflectionProvider;
	}

	/**
	 * HierarchicalStreamDriver
	 */
	private HierarchicalStreamDriver hierarchicalStreamDriver = null;
	/**
	 * ClassLoader
	 */
	private ClassLoader classLoader = null;
	/**
	 * mapper
	 */
	private Mapper mapper = null;
	/**
	 * ConverterLookup
	 */
	private ConverterLookup converterLookup = null;
	/**
	 * ConverterRegistry
	 */
	private ConverterRegistry converterRegistry = null;

	/**
	 * 忽略未知属性
	 */
	private boolean ignoreUnknownFields;

	public boolean isIgnoreUnknownFields() {
		return ignoreUnknownFields;
	}

	public void setIgnoreUnknownFields(boolean ignoreUnknownFields) {
		this.ignoreUnknownFields = ignoreUnknownFields;
	}

	/**
	 * implicit colletion/type map, consisting of implicit collection String
	 * keys
	 */
	@SuppressWarnings("unchecked")
	private Map<String, Class> implicitCollection;

	@SuppressWarnings("unchecked")
	public Map<String, Class> getImplicitCollection() {
		return implicitCollection;
	}

	@SuppressWarnings("unchecked")
	public void setImplicitCollection(Map<String, Class> implicitCollection) {
		this.implicitCollection = implicitCollection;
	}

	/**
	 * omitted fields, as a Map consisting of <code>Class</code> instances
	 * mapped to comma separated field names.
	 */
	@SuppressWarnings("unchecked")
	private Map<Class, String> omittedFields;

	@SuppressWarnings("unchecked")
	public Map<Class, String> getOmittedFields() {
		return omittedFields;
	}

	@SuppressWarnings("unchecked")
	public void setOmittedFields(Map<Class, String> omittedFields) {
		this.omittedFields = omittedFields;
	}

	/**
	 * classes for which mappings will be read from class-level JDK 1.5+
	 * annotation metadata.
	 */
	private Class<?>[] annotatedClasses;

	private XStream instance = null;

	/**
	 * 创建XStream
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public void init() {
		// new DomDriver();
		if (classLoader == null) {
			classLoader = Thread.currentThread().getContextClassLoader();
		}

		if (ignoreUnknownFields) {
			instance = new XStream(reflectionProvider,
					hierarchicalStreamDriver, classLoader, mapper,
					converterLookup, converterRegistry) {
				protected MapperWrapper wrapMapper(MapperWrapper next) {
					return new MapperWrapper(next) {
						@SuppressWarnings("unchecked")
						public boolean shouldSerializeMember(Class definedIn,
								String fieldName) {
							return definedIn != Object.class ? super
									.shouldSerializeMember(definedIn, fieldName)
									: false;
						}
					};
				}

			};
		} else {
			instance = new XStream(reflectionProvider,
					hierarchicalStreamDriver, classLoader, mapper,
					converterLookup, converterRegistry);
		}

		if (mode > 0) {
			instance.setMode(mode);
		}

		//
		if (enableHibernatePO) {
			instance.addDefaultImplementation(
					org.hibernate.collection.PersistentList.class,
					java.util.List.class);
			instance.addDefaultImplementation(
					org.hibernate.collection.PersistentMap.class,
					java.util.Map.class);
			instance.addDefaultImplementation(
					org.hibernate.collection.PersistentSet.class,
					java.util.Set.class);
			instance.addDefaultImplementation(
					org.hibernate.collection.PersistentBag.class,
					java.util.List.class);
			instance.addDefaultImplementation(
					org.hibernate.collection.PersistentIdentifierBag.class,
					java.util.List.class);
			instance.addDefaultImplementation(
					org.hibernate.collection.PersistentSortedMap.class,
					java.util.SortedMap.class);
			instance.addDefaultImplementation(
					org.hibernate.collection.PersistentSortedSet.class,
					java.util.SortedSet.class);
		}

		// 别名
		if (aliases != null && aliases.size() > 0) {
			for (Map.Entry<String, Class> entry : aliases.entrySet()) {
				instance.alias(entry.getKey(), entry.getValue());
			}
		}
		// //aliasField
		// if(aliasField!=null && aliasField.size()>0){
		// Iterator<String> ks = aliasField.keySet().iterator();
		// while(ks.hasNext()){
		// String k = ks.next();
		// FieldMap map = aliasField.get(k);
		// instance.useAttributeFor( map.getClzz(), map.getFieldTo());
		// instance.aliasField(map.getFieldFrom(), map.getClzz(),
		// map.getFieldTo());
		// }
		// }
		//		
		//		
		if (attributes != null && attributes.size() > 0) {
			for (Map.Entry<?, ?> entry : attributes.entrySet()) {
				if (entry.getKey() instanceof String
						&& entry.getValue() instanceof Class) {
					instance.useAttributeFor((String) entry.getKey(),
							(Class) entry.getValue());
				} else if (entry.getKey() instanceof Class
						&& entry.getValue() instanceof String) {
					instance.useAttributeFor((Class) entry.getKey(),
							(String) entry.getValue());
				} else {
					throw new IllegalArgumentException(
							"Invalid attribute key and value pair. "
									+ "'useAttributesFor' property takes either a <String, Class> map or a <Class, String> map");
				}
			}
		}
		// 转换器
		if (converters != null && converters.size() > 0) {
			for (int i = 0; i < converters.size(); i++) {
				if (converters.get(i) instanceof Converter) {
					this.instance.registerConverter((Converter) converters
							.get(i), i);
				} else if (converters.get(i) instanceof SingleValueConverter) {
					this.instance.registerConverter(
							(SingleValueConverter) converters.get(i), i);
				} else {
					throw new IllegalArgumentException(
							"Invalid ConverterMatcher [" + converters.get(i)
									+ "]");
				}
			}
		}

		if (useAttributeForTypes != null && useAttributeForTypes.size() > 0) {
			for (Class type : useAttributeForTypes) {
				instance.useAttributeFor(type);
			}
		}

		if (implicitCollection != null && implicitCollection.size() > 0) {
			for (Map.Entry<String, Class> entry : implicitCollection.entrySet()) {
				instance
						.addImplicitCollection(entry.getValue(), entry.getKey());
			}
		}
		if (omittedFields != null && omittedFields.size() > 0) {
			for (Map.Entry<Class, String> entry : omittedFields.entrySet()) {
				String[] fields = StringUtils
						.commaDelimitedListToStringArray(entry.getValue());
				for (String field : fields) {
					instance.omitField(entry.getKey(), field);
				}
			}
		}
		if (annotatedClasses != null) {
			instance.processAnnotations(annotatedClasses);
		}

		// aliasPkg
		// if(aliasPkg!=null && aliasPkg.size()>0){
		// Iterator<String> ks = aliasPkg.keySet().iterator();
		// while(ks.hasNext()){
		// String k = ks.next();
		// instance.aliasPackage(k, aliasPkg.get(k));
		// //instance.a
		// }
		// }

		// <dirty>false</dirty>
		instance.omitField(org.hibernate.engine.CollectionEntry.class, "dirty");
		if (enableHibernatePO) {
			Mapper mapper = instance.getMapper();
			instance
					.registerConverter(new HibernateCollectionConverter(mapper));
			instance.registerConverter(new HibernateMapConverter(mapper));
		}

	}

	public HierarchicalStreamDriver getHierarchicalStreamDriver() {
		return hierarchicalStreamDriver;
	}

	public void setHierarchicalStreamDriver(
			HierarchicalStreamDriver hierarchicalStreamDriver) {
		this.hierarchicalStreamDriver = hierarchicalStreamDriver;
	}

	public ClassLoader getClassLoader() {
		return classLoader;
	}

	public void setClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	public Mapper getMapper() {
		return mapper;
	}

	public void setMapper(Mapper mapper) {
		this.mapper = mapper;
	}

	public ConverterLookup getConverterLookup() {
		return converterLookup;
	}

	public void setConverterLookup(ConverterLookup converterLookup) {
		this.converterLookup = converterLookup;
	}

	public ConverterRegistry getConverterRegistry() {
		return converterRegistry;
	}

	public void setConverterRegistry(ConverterRegistry converterRegistry) {
		this.converterRegistry = converterRegistry;
	}

	@SuppressWarnings("unchecked")
	public boolean supports(Class clazz) {
		if (ObjectUtils.isEmpty(this.supportedClasses)) {
			return true;
		} else {
			for (Class supportedClass : this.supportedClasses) {
				if (supportedClass.isAssignableFrom(clazz)) {
					return true;
				}
			}
			return false;
		}
	}

	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	public Map<?, ?> getAttributes() {
		return attributes;
	}

	public void setAttributes(Map<?, ?> attributes) {
		this.attributes = attributes;
	}

	public Class<?>[] getAnnotatedClasses() {
		return annotatedClasses;
	}

	public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
		this.annotatedClasses = annotatedClasses;
	}

	public Object getObject() throws Exception {
		return instance;
	}

	@SuppressWarnings("unchecked")
	public Class getObjectType() {
		return XStream.class;
	}

	public boolean isSingleton() {
		return true;
	}
}
